__all__ = ['Fetcher']
class Fetcher:
    """
    Fetcher instances are parameterized factories of iterables over fetched data.

    Instances are constructed in main scripts and passed to storages this way:
        fetcher_instance = SomeFetcherClass(opt1 = ..., opt2 = ...)
        any_storage = AnyStorageClass(fetcher_instance)

    And they are called from storages to construct an iterable this way:
        iterable = fetcher_instance(fields, entity, ...)
        for row in iterable: do_something(row)

    So fetcher class defines __init__() to construct a fethcer itself, and __call__() to construct an iterable.
    It uses read() to retrieve iterable source of original data, and then cycles over it and cast()s values.
    """

    def __init__(self, **opts):
        self.opts = opts

    def __call__(self, fields, *args):
        for row in self.read(fields, *args, **self.opts):
            res = {}
            for field in fields.keys():
                res[field] = self.cast(fields[field], row[field])
            yield res

    def read(self, fields, *args, **opts):
        raise NotImplementedError

    def cast(self, caster, value):
        raise NotImplementedError
